﻿(*
Haskell code from http://hackage.haskell.org/packages/archive/citeproc-hs/0.2/doc/html/src/Text-CSL-Proc-Collapse.html
-- | Group consecutive integers:
--
-- > groupConsec [1,2,3,5,6,8,9] == [[1,2,3],[5,6],[8,9]]
groupConsec :: [Int] -> [[Int]]
groupConsec = groupConsec' []
    where
      groupConsec' x   []    = x
      groupConsec' [] (y:ys) = groupConsec' [[y]] ys
      groupConsec' xs (y:ys) = if y - head (last xs) == length (last xs)
                               then groupConsec' (init xs ++ [last xs ++ [y]]) ys
                               else groupConsec' (     xs ++ [           [y]]) ys
*)
module GroupConsecutiveIntegers

open FsUnit
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Collections
    
let groupConsec =
    let head = List.head
    let last l = List.nth l (l.Length-1)
    let length = List.length
    let init l = List.ofSeq (Seq.take ((List.length l)-1) l)
    let rec groupConsec' a b =
        match a,b with
        | x, [] -> x
        | [], y::ys -> groupConsec' [[y]] ys
        | xs, y::ys -> if y - head (last xs) = length (last xs)
                       then groupConsec' (init xs @ [last xs @ [y]]) ys
                       else groupConsec' (     xs @ [          [y]]) ys
                       
    groupConsec' []

let lazyGroupConsec =
    let head = LazyList.head
    /// From Haskell prelude: http://www.haskell.org/onlinereport/standard-prelude.html
    let rec last =
            function
            | LazyList.Nil -> failwith "last: empty list"
            | LazyList.Cons(x,xs) -> 
            match xs with
            | LazyList.Nil -> x
            | _ -> last xs
            
    let length = LazyList.length
    let rec init = 
            function
            | LazyList.Nil -> failwith "init: empty list"
            | LazyList.Cons(x,xs) -> 
            match xs with
            | LazyList.Nil -> LazyList.empty
            | _ -> LazyList.cons x (init xs)
            
    let (@) x y = LazyList.append x y
    let lazyList x = LazyList.cons x LazyList.empty
                
    let rec groupConsec' a b =
        match a,b with
        | x, LazyList.Nil -> x
        | LazyList.Nil, LazyList.Cons(y,ys) -> groupConsec' (lazyList (LazyList.ofList [y])) ys
        | xs, LazyList.Cons(y,ys) -> if y - head (last xs) = length (last xs)
                                     then groupConsec' (init xs @ lazyList (last xs @ (LazyList.ofList [y]))) ys
                                     else groupConsec' (     xs @ lazyList (          (LazyList.ofList [y]))) ys

    groupConsec' LazyList.empty

let lazyGroupConsec2 l =
    let lastOf x = List.nth x ((List.length x) - 1)
    let rec groupConsec' group groups input =
            if Seq.isEmpty input
            then Seq.append groups [group]
            else
                let hd = Seq.head input
                let tl = Seq.skip 1 input
                if List.length group = 0 || hd - (lastOf group) <= 1
                then groupConsec' (group @ [hd]) groups tl
                else groupConsec' [hd] (Seq.append groups [group]) tl
    groupConsec' [] [] l

let lazyGroupConsec3 l =
    let lastOf x = List.nth x ((List.length x) - 1)
    let (+) x y = LazyList.append x (LazyList.ofList [y])
    let rec groupConsec' group groups input =
            if LazyList.isEmpty input
            then groups + group
            else
                let hd = LazyList.head input
                let tl = LazyList.tail input
                if List.length group = 0 || hd - (lastOf group) <= 1
                then groupConsec' (group @ [hd]) groups tl
                else groupConsec' [hd] (groups + group) tl
    groupConsec' [] LazyList.empty (LazyList.ofSeq l)
